package com.deer.wms.intercept.config;

import com.deer.wms.intercept.common.data.CommonDataService;
import com.deer.wms.intercept.filter.TokenAuthFilter;
import com.deer.wms.intercept.filter.TokenLoginFilter;
import com.deer.wms.intercept.security.DefaultPasswordEncoder;
import com.deer.wms.intercept.security.JwtUtil;
import com.deer.wms.intercept.security.TokenLogoutHandler;
import com.deer.wms.intercept.security.UnauthEntryPoint;
import com.deer.wms.project.root.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsConfigurationSource;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.Arrays;

/**
 * @author : wei
 * @since : 2021-07-30 14:31:42
 **/
@Configuration
@EnableWebSecurity
//启用方法安全设置
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private final JwtUtil jwtUtil;
    private final RedisUtil redisUtil;
    private final UserDetailsService userDetailsService;
    private final DefaultPasswordEncoder defaultPasswordEncoder;
    private final CorsFilter corsFilter;
    private CommonDataService commonDataService;

    @Autowired
    public SecurityConfig(UserDetailsService userDetailsService, DefaultPasswordEncoder defaultPasswordEncoder,
                          JwtUtil jwtUtil, RedisUtil redisUtil, CorsFilter corsFilter, CommonDataService commonDataService) {
        this.userDetailsService = userDetailsService;
        this.defaultPasswordEncoder = defaultPasswordEncoder;
        this.jwtUtil = jwtUtil;
        this.redisUtil = redisUtil;
        this.corsFilter = corsFilter;
        this.commonDataService = commonDataService;
    }

    /**
     * 通过HTTP对象的authorizeRequests()方法定义URL访问权限，默认为formLogin()提供的一个简单的登录验证页面
     *
     * @param http
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.exceptionHandling()
                .authenticationEntryPoint(new UnauthEntryPoint())//没有权限访问
                .and()
                /*定义哪些URL要被保护*/
                .authorizeRequests()
                .anyRequest().authenticated()
                /*登出配置*/
                .and().logout().logoutUrl("/admin/acl/index/logout")//退出路径
                .addLogoutHandler(new TokenLogoutHandler(jwtUtil, redisUtil)).and()
                .addFilter(new TokenLoginFilter(authenticationManager(), jwtUtil, redisUtil))
                .addFilter(new TokenAuthFilter(authenticationManager(), jwtUtil, redisUtil, commonDataService)).httpBasic();
        /*关闭csrf防护*/
        http.csrf().disable().addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class);

    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    /**
     * 通过auth对象的方法添加身份验证。
     * 调用userDetailsService和密码处理
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(defaultPasswordEncoder);
    }

    /**
     * 设置忽略权限的静态资源。不进行认证的路径，可以直接访问
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/**/*.js", "/**/*.css", "/images/**", "/fonts/**","/**/*.html", "favicon.ico");
//可以看到，以api为开头的接口也在里面，这表示WebSecurity可以过滤前后端的资源，只是更好的是比较适合前端过滤
        web.ignoring().antMatchers("/sys/params/findByMemo", "/company/add","/users/validUserAccount","/ureport/**",
                "/swagger-resources/**","/v3/**","/swagger-ui/**", "/swagger-ui", "/doc.html", "/webjars/**",
                "/print/**", "/common/getAppVer", "/common/download/app",
                        "/out/master/sign/**", "/heartbeat/heartbeat");
    }
}
